package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.huaweicloud.sdk.core.auth.AbstractCredentials;
import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
import com.huaweicloud.sdk.core.exception.ServiceResponseException;
import com.huaweicloud.sdk.iotda.v5.model.*;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.framework.config.properties.HuaWeiIotConfigProperties;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.task.vo.IotMsgNotifyData;
import com.zzyl.nursing.task.vo.IotMsgService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.DeviceReoprtDataVo;
import com.zzyl.nursing.vo.ProductVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

/**
 * 设备表Service业务层处理
 *
 * @author jby
 * @date 2025-08-21
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IoTDAClient client;

    /**
     * 同步设备列表
     *
     * @param
     * @return
     */
    @Override
    public void asycDevice() {
        // 1.华为云上获取数据
        ListProductsRequest request = new ListProductsRequest();
        ListProductsResponse response = client.listProducts(request);
        // 2. 存入REDIs缓存
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("同步设备失败");
        }
        // 3. 存入数据库
        try {
            redisTemplate.opsForValue().set(Constants.CACHE_IOT_PRODUCTS_ALL, JSONUtil.toJsonStr(response.getProducts()));
        } catch (Exception e) {
            log.error("同步设备写入缓存失败:{}", e);
        }
    }

    /**
     * 查询所有产品
     *
     * @param
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        String cache = (String) redisTemplate.opsForValue().get(Constants.CACHE_IOT_PRODUCTS_ALL);
        if (StrUtil.isEmpty(cache)) {
            return List.of();
        }
        return JSONUtil.toList(cache, ProductVo.class);
    }

    /**
     * 注册设备
     *
     * @param deviceDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int register(DeviceDto deviceDto) {
        // 1.判断设备名是否重复
        isExistDeviceName(deviceDto.getDeviceName());
        // 2. 判断设备标识是否重复
        isExistNodeId(deviceDto.getNodeId());
        // 3. 判断位置是否绑定同一设备
        if (ObjUtil.isNull(deviceDto.getPhysicalLocationType())) {
            deviceDto.setPhysicalLocationType(-1);
        }
        isExistBindingLocation(deviceDto.getProductKey(), deviceDto.getBindingLocation(), deviceDto.getLocationType(), deviceDto.getPhysicalLocationType());
        // 4. 注册设备到华为IOT
        AddDeviceResponse response = saveDeviceToIot(deviceDto);
        // 5. 数据库保存设备
        saveDevice(deviceDto, response.getDeviceId(), response.getAuthInfo().getSecret());
        return 1;
    }

    //保存设备到数据库
    private void saveDevice(DeviceDto deviceDto, String deviceId, String secret) {
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(deviceId);
        device.setSecret(secret);
        if (deviceDto.getPhysicalLocationType().equals(-1)) {
            device.setHaveEntranceGuard(0);
        } else {
            device.setHaveEntranceGuard(1);
        }
        try {
            save(device);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            //TODO 删除IOT平台关联的设备
            throw new BaseException("保存设备到数据库失败了");
        }
    }

    /**
     * 保存设备到IOT
     *
     * @param deviceDto
     * @return
     */
    private AddDeviceResponse saveDeviceToIot(DeviceDto deviceDto) {
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice addDevice = new AddDevice();
        AuthInfo authInfo = new AuthInfo();
        authInfo.withAuthType("SECRET")
                .withSecret(RandomUtil.randomString(32))
                .withSecureAccess(true);
        //IOT平台会在正价设备后, 自动生成设备ID
        addDevice.withNodeId(deviceDto.getNodeId())
                .withDeviceName(deviceDto.getDeviceName())
                .withProductId(deviceDto.getProductKey())
                .withDescription(deviceDto.getDeviceDescription())
                .withAuthInfo(authInfo);
        request.withBody(addDevice);
        AddDeviceResponse response = client.addDevice(request);
        if (response.getHttpStatusCode() != 201) {
            throw new BaseException("设备注册失败");
        }
        return response;
    }

    //判断位置是否绑定同一设备
    private void isExistBindingLocation(String productKey, Long bindingLocation, Integer locationType, Integer physicalLocationType) {
        if (this.lambdaQuery()
                .eq(Device::getProductKey, productKey)
                .eq(Device::getBindingLocation, bindingLocation)
                .eq(Device::getLocationType, locationType)
                .eq(Device::getPhysicalLocationType, physicalLocationType)
                .count() > 0) {
            throw new BaseException("绑定位置已存在");
        }
    }

    //判断设备标识是否重复
    private void isExistNodeId(String nodeId) {
        if (this.lambdaQuery().eq(Device::getProductKey, nodeId).count() > 0) {
            throw new BaseException("设备标识已存在");
        }
    }

    //判断设备名是否重复
    private void isExistDeviceName(String deviceName) {
        if (this.lambdaQuery().eq(Device::getDeviceName, deviceName).count() > 0) {
            throw new BaseException("设备名已存在");
        }
    }

    /**
     * 查询设备表列表
     *
     * @param device 设备表
     * @return 设备表
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(device.getDeviceName()), Device::getDeviceName, device.getDeviceName())
                .eq(StrUtil.isNotBlank(device.getProductName()), Device::getProductName, device.getProductName())
                .eq(ObjUtil.isNotNull(device.getLocationType()), Device::getLocationType, device.getLocationType());
        List<Device> list = this.list(queryWrapper);
        return list;
    }

    /**
     * 根据ID查询
     *
     * @param
     * @return
     */
    @Override
    public DeviceDetailVo getByIotId(String iotId) {
        // 1. 从iot上获取产品和设备详情
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        String status = response.getStatus();
        String activeTimeStr = response.getActiveTime();
        // 2. 根据设备ID从数据库中获取设备详情
        Device one = this.lambdaQuery().eq(Device::getIotId, iotId).one();

        // 3. 合并两个数据为DeviceDetailVo
        DeviceDetailVo deviceDetailVo = new DeviceDetailVo();
        BeanUtils.copyProperties(one, deviceDetailVo);
        deviceDetailVo.setDeviceStatus(status);
        // 3.1 转换时间格式和时区
        try {
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.systemDefault())
                    .toLocalDateTime();
            deviceDetailVo.setActiveTime(activeTime);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return deviceDetailVo;
    }

    /**
     * 查询设备上报数据
     *
     * @param iotId
     * @return
     */
    @Override
    public List<DeviceReoprtDataVo> queryServiceProperties(String iotId) {
        List<DeviceReoprtDataVo> list = new ArrayList<>();
        try {
            // 1.从IOT上获取数据
            ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
            request.withDeviceId(iotId);
            ShowDeviceShadowResponse response = client.showDeviceShadow(request);
            // 1.1判断响应码
            if (response.getHttpStatusCode() != 200) {
                throw new BaseException("获取数据失败.");
            }
            // 1.2 校验数据
            List<DeviceShadowData> shadow = response.getShadow();
            if (ArrayUtil.isEmpty(shadow)) {
                throw new BaseException("获取数据失败.");
            }
            // 2. 封装数据
            shadow.forEach(s -> {
                String eventTime1 = s.getReported().getEventTime();
                Map<String, Object> map = (Map) s.getReported().getProperties();
                map.forEach((k, v) -> {
                    LocalDateTime eventTime = null;
                    if (StrUtil.isNotBlank(eventTime1)) {
                        eventTime = LocalDateTimeUtil.parse(eventTime1, Constants.LOCAL_DATE_PATTERN)
                                .atZone(ZoneId.from(ZoneOffset.UTC))
                                .withZoneSameInstant(ZoneId.systemDefault())
                                .toLocalDateTime();
                    }
                    DeviceReoprtDataVo deviceReoprtDataVo = new DeviceReoprtDataVo();
                    deviceReoprtDataVo.setFunctionId(k);
                    deviceReoprtDataVo.setEventTime(eventTime);
                    deviceReoprtDataVo.setValue(v);
                    list.add(deviceReoprtDataVo);
                });
            });
        } catch (Exception e) {
            throw new BaseException(e.getMessage());
        }
        return list;
    }

    /**
     * 删除设备
     *
     * @param iotId
     * @return void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDevice(String iotId) {
        //先从数据库删除设备
        deviceMapper.deleteDeviceByIotId(iotId);
        //再从IOT删除设备
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
            System.out.println(response.toString());
        } catch (ConnectionException e) {
            e.printStackTrace();
        } catch (RequestTimeoutException e) {
            e.printStackTrace();
        } catch (ServiceResponseException e) {
            e.printStackTrace();
            System.out.println(e.getHttpStatusCode());
            System.out.println(e.getRequestId());
            System.out.println(e.getErrorCode());
            System.out.println(e.getErrorMsg());
        }
    }

    /**
     * 修改设备
     *
     * @param deviceDto
     * @return void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDevice(DeviceDto deviceDto) {

        //修改数据库中的设备
        Device device = new Device();
        BeanUtils.copyProperties(deviceDto, device);
        this.updateById(device);

        //修改华为IOT的设备
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(deviceDto.getIotId());
        UpdateDevice body = new UpdateDevice();
        AuthInfoWithoutSecret authInfobody = new AuthInfoWithoutSecret();
        authInfobody.withSecureAccess(true);
        body.withAuthInfo(authInfobody);
        body.withDeviceName(deviceDto.getDeviceName());
        request.withBody(body);

        try {
            UpdateDeviceResponse response = client.updateDevice(request);
            System.out.println(response.toString());
        } catch (ConnectionException e) {
            e.printStackTrace();
        } catch (RequestTimeoutException e) {
            e.printStackTrace();
        } catch (ServiceResponseException e) {
            e.printStackTrace();
            System.out.println(e.getHttpStatusCode());
            System.out.println(e.getRequestId());
            System.out.println(e.getErrorCode());
            System.out.println(e.getErrorMsg());
        }
    }

    /**
     * 查询产品详情(报警规则模块使用)
     *
     * @param
     * @return
     */
    @Override
    public List<ServiceCapability> queryProduct(String productKey) {
        ShowProductRequest request = new ShowProductRequest();
        request.withProductId(productKey);
        ShowProductResponse response = client.showProduct(request);
        if(response.getHttpStatusCode() != 200){
            throw new BaseException("获取数据失败.");
        }
        return response.getServiceCapabilities();
    }
}
